package org.jeecg.common.exception;

import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.cache.PermissionCodeCache;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.SysLogOperateType;
import org.jeecg.common.constant.enums.SysLogType;
import org.jeecg.common.constant.enums.SysLogUrlOperate;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.LogUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.vo.PermissionsInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.connection.PoolException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.gcloud.mesh.framework.core.web.handle.ErrorCodeMapper;
import com.gcloud.mesh.header.exception.BaseException;
import com.gcloud.mesh.header.exception.CommonErrorCode;

import lombok.extern.slf4j.Slf4j;

/**
 * 异常处理器
 * 
 * @Author scott
 * @Date 2019
 */
@RestControllerAdvice
@Slf4j
public class JeecgBootExceptionHandler {

	@Autowired
	private ErrorCodeMapper errorCodeMapper;
	@Autowired
	private ISysBaseAPI sysBaseAPI;

	/**
	 * 处理自定义异常
	 */
	@ExceptionHandler(JeecgBootException.class)
	public Result<?> handleRRException(JeecgBootException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.DATABASE_ERROR);

		return Result.error(e.getMessage());
	}
	/**
	 * 用于参数检验打日志
	 * @param e
	 * @return
	 */
	@ExceptionHandler({BindException.class, ParamException.class})
	public Result<?> handleBindException(Exception e, HttpServletRequest request) {
	   log.error(e.getMessage(), e);
	   String errorMessage = null;
	   String errorCode = null;
	   if(e instanceof BindException) {
		   errorCode = ((BindException) e).getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
			try {
				errorMessage = this.errorCodeMapper.getErrorMsg(errorCode);
			} catch(Exception ee) {
				e.printStackTrace();
			}
	   }else {
		   errorCode = ((ParamException) e).getErrorCode();
		   errorMessage = ((ParamException) e).getErrorMsg();
		   if(errorMessage == null) {
			   errorMessage = this.errorCodeMapper.getErrorMsg(errorCode);
			   if(errorMessage == null) {
				   errorMessage = this.errorCodeMapper.getErrorMsg(errorCode);
			   }
		   }
	   }
	   if (!StringUtils.isNotBlank(errorCode)) {
			errorCode = CommonErrorCode.UNKNOWN_ERROR;
		}
		if (StringUtils.isBlank(errorMessage)) {
			errorMessage = "接口请求错误,请联系管理员";
		}
//		String requestUrl = request.getRequestURI().replace("//", "/");
//		if (requestUrl.startsWith("/mesh-controller")) {
//			requestUrl = requestUrl.substring("/mesh-controller".length(), requestUrl.length());
//		}
//		PermissionsInfoVo vo = PermissionCodeCache.getByUrl(requestUrl);
//		Integer logType = SysLogUrlOperate.getOperateTypeByUrl(requestUrl);
//		if(vo != null) {
//			//获取title
//			String title = request.getParameter("title");
//			if(StringUtils.isNotBlank(title)) {
//				sysBaseAPI.addLog(vo.getName()+"失败（"+errorMessage+"）", SysLogType.OPERATE, SysLogOperateType.getByType(logType), null, SysLogUrlOperate.errorRemark ,title);
//			}else {
//				sysBaseAPI.addLog(vo.getName()+"失败（"+errorMessage+"）", SysLogType.OPERATE,  SysLogOperateType.getByType(logType), null, SysLogUrlOperate.errorRemark, null);
//			}			
//		}
		LogUtil.log(request, SysLogUrlOperate.errorRemark+":"+errorMessage);
		if(errorMessage == null) {
			errorMessage = errorCode;
		}
		return Result.error(errorMessage);
	}

	@ExceptionHandler(NoHandlerFoundException.class)
	public Result<?> handlerNoFoundException(Exception e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.CONFIG_ERROR);

		return Result.error(404, "路径不存在，请检查路径是否正确");
	}

	@ExceptionHandler(DuplicateKeyException.class)
	public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.DATABASE_ERROR);

		return Result.error("数据库中已存在该记录");
	}

	@ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class })
	public Result<?> handleAuthorizationException(AuthorizationException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.PROCEDURE_ERROR);

		return Result.noauth("没有权限，请联系管理员授权");
	}

	@ExceptionHandler(MyBusinessException.class)
	public Result<?> handlerMyBusinessException(MyBusinessException e){
		handleLog(e, SysLogOperateType.PROCEDURE_ERROR);
		return Result.error(e.getErrorMsg());
	}

	@ExceptionHandler(Exception.class)
	public Result<?> handleException(Exception e) {

		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.PROCEDURE_ERROR);

		String errorCode = null;
		String errorMessage = null;

		if (e instanceof BaseException) {
			errorCode = ((BaseException) e).getErrorCode();
			errorMessage = ((BaseException) e).getErrorMsg();

		} else if (e instanceof MethodArgumentNotValidException) {
			List<FieldError> fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
			if (fieldErrors != null && fieldErrors.size() > 0) {
				errorCode = fieldErrors.get(0).getDefaultMessage();
			} else {
				errorCode = CommonErrorCode.UNKNOWN_ERROR;
			}
		} else if (e instanceof NumberFormatException) {
			errorMessage = "参数类型错误";
		} else {
			errorCode = CommonErrorCode.UNKNOWN_ERROR;
		}

		if (errorMessage == null) {
			try {
				errorMessage = this.errorCodeMapper.getErrorMsg(errorCode);
			} catch(Exception ee) {
				e.printStackTrace();
			}
			if (StringUtils.isBlank(errorMessage)) {
				errorMessage = "接口请求错误,请联系管理员";
			}
			
		}

//		return Result.error("操作失败，" + errorMessage);
		
		return Result.error(errorMessage);
	}

	/**
	 * @Author 政辉
	 * @param e
	 * @return
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public Result<?> HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
		StringBuffer sb = new StringBuffer();
		sb.append("不支持");
		sb.append(e.getMethod());
		sb.append("请求方法，");
		sb.append("支持以下");
		String[] methods = e.getSupportedMethods();
		if (methods != null) {
			for (String str : methods) {
				sb.append(str);
				sb.append("、");
			}
		}
		log.error(sb.toString(), e);
		// return Result.error("没有权限，请联系管理员授权");

		handleLog(e, SysLogOperateType.PROCEDURE_ERROR);

		return Result.error(405, sb.toString());
	}

	/**
	 * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
	 */
	@ExceptionHandler(MaxUploadSizeExceededException.class)
	public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.PROCEDURE_ERROR);

		return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
	}

	@ExceptionHandler(DataIntegrityViolationException.class)
	public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.DATABASE_ERROR);

		return Result.error("字段太长,超出数据库字段的长度");
	}

	@ExceptionHandler(PoolException.class)
	public Result<?> handlePoolException(PoolException e) {
		log.error(e.getMessage(), e);

		handleLog(e, SysLogOperateType.DATABASE_ERROR);

		return Result.error("Redis 连接异常!");
	}

	private void handleLog(Exception e, SysLogOperateType operateType) {
//		ISysBaseAPI sysBaseAPI = (ISysBaseAPI) SpringContextUtils.getBean("sysBaseAPI");
//		String logContent = getLogContent(e);
//		if (StringUtils.isNotBlank(logContent)) {
//			if (SysLogOperateType.PROCEDURE_ERROR != operateType) {
//				sysBaseAPI.addLog(logContent, SysLogType.ERROR, operateType, null, e.getMessage());
//			}
//		}
	}

	private String getLogContent(Exception e) {
		String logContent = null;
		java.lang.StackTraceElement[] classArray = e.getStackTrace();
		for (int i = 0; i < classArray.length; i++) {
			String classname = classArray[i].getClassName();
			String methodname = classArray[i].getMethodName();
			// log.info("调用数据源的类名：【" + classname + "】，方法名：【" + methodname +
			// "】");
			if (StringUtils.isNotBlank(classname) && classname.endsWith("Controller")) {
				try {
					Class<?> controllerClass = Class.forName(classname);
					if (controllerClass != null) {
						for (Method method : controllerClass.getMethods()) {
							if (method.getName().equals(methodname)) {
								AutoLog log = method.getAnnotation(AutoLog.class);
								if (log != null) {
									logContent = log.value();
									break;
								}
							}
						}
					}
				} catch (ClassNotFoundException e1) {
					break;
				}
			}
		}
		return logContent;
	}

}
